package org.apm.data.pusher;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class Config {
    // ES type
    // metedata
    public static final String TYPE_JAVA = "JavaTraceData";
    public static final String TYPE_STACK = "JavaStackTraceData";
    public static final String TYPE_JVM = "JavaManagementData";
    public static final String TYPE_LOGM = "LogMessageData";
    public static final String TYPE_SNIFFER = "SnifferData";
    public static final String TYPE_SYSTEMMONIT = "SystemData";
    public static final String TYPE_URLRECORDDATA = "UrlRecordData";
    // data type of the big-json
    public static final String TYPE_EVENT = "Event";

    public static final Set<String> TYPES_METETYPES = new HashSet<>();
    // generator results
    public static final String TYPE_DB = "DBType";
    public static final String TYPE_RELATIONSHIP = "RelationShip";
    public static final String TYPE_RESPONSE = "Response";
    public static final String TYPE_BASELINE = "BaseLine";
    public static final String TYPE_SLOWMETHOD = "SlowMethod";
    public static final String TYPE_MONITMETHOD = "MonitMethod";
    public static final String TYPE_JSPAGE = "pagemonitor";
    public static final String TYPE_JSRESOURCE = "resourcesmonitor";

    // ES flag
    public static final int FLAG_UNREAD = 0;
    public static final int FLAG_READ = 1;
    public static final String MODULE_AVG = "1";
    public static final String MODULE_TP = "2";
    public static final String PREFIX_DEFAULTMESSAGE = "DefaultMessage.";
    public static final String PREFIX_EXCEPTIONMESSAGE = "ExceptionMessage.";
    public static final String PREFIX_STACKTRACEMESSAGE = "stackTraceMessage.";
    public static final int SIZE = 5;

    public static final String COMBINE_RATIO;

    // IP
    public static final int MAX_IP_PART = 255;
    public static final int MAX_PORT = 65535;
    
    //Channel
    public static final String CHANNEL_TYPE_INTERNAL = "内部来源";
    public static final String CHANNEL_TYPE_EXTERNAL = "外部链接";
    public static final String CHANNEL_TYPE_SEARCH = "搜索引擎";
    public static final String CHANNEL_TYPE_DIRECT = "直接访问";

    public static final int DEF_PORT = 9300;
    public static final String clusterName;
    public static final String clientModel;
    public static final String hostStr;
    public static final String apmIndex;
    public static final String eventJsonIndex;
    public static Map<String, Integer> hosts = new HashMap<>();

    private static final Logger LOG = LoggerFactory.getLogger(Config.class);
    private static final String ESCONNECT_PROPERTIES = "config.properties";
    private static final String USER_DEFINED_PROPERTIES = "esclient.properties";

    static {
        Properties props = readProperties();
        clusterName = props.getProperty("es_cluster_name", "Apm");
        clientModel = props.getProperty("es_client_mode", "TRANSPORT");
        hostStr = props.getProperty("es_host_address", "[localhost]");
        apmIndex = props.getProperty("es_index_prefix", "apm2.0-");
        eventJsonIndex = props.getProperty("es_index_prefix_event_data");

        COMBINE_RATIO = props.getProperty("combineRatio", "10");

        // System.out.println("读取自己的配置文件:" + eventJsonIndex);
        try {
            getHosts(hostStr);
        } catch (Exception exception) {
            exception.printStackTrace();
        }

        TYPES_METETYPES.add(TYPE_JAVA);
        TYPES_METETYPES.add(TYPE_STACK);
        TYPES_METETYPES.add(TYPE_JVM);
        TYPES_METETYPES.add(TYPE_LOGM);
        TYPES_METETYPES.add(TYPE_SNIFFER);
        TYPES_METETYPES.add(TYPE_SYSTEMMONIT);
        TYPES_METETYPES.add(TYPE_URLRECORDDATA);
        TYPES_METETYPES.add(TYPE_EVENT);

        LOG.info("elasticsearch-client: { clusterName: " + clusterName + " ,clusterModel: " + clientModel + " ,apmIndex: " + apmIndex + " ,host: " + hosts + "}");
    }

    private Config() {
    }

    private static void getHosts(final String hostString) throws Exception  {
        String hostStr = hostString.substring(1, hostString.length() - 1);
        hostStr = hostStr.replace(" ", "");
        for (String host : hostStr.split(",")) {
            String ip = "";
            int port = DEF_PORT;
            if (host.contains(":")) {
                String[] part = host.split(":");
                ip = part[0];
                port = Integer.valueOf(part[1]);
                if (!isIpFormat(ip, port)) {
                    continue;
                }
            } else {
                ip = host;
            }
            hosts.put(ip, port);
        }
        if (hosts.size() < 1) {
            throw new Exception("incorrect host.");
        }
    }

    private static boolean isIpFormat(final String ip, final int port) {
        boolean isIpFormat = false;
        if (ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {
            String[] str = ip.split("\\.");
            if (Integer.parseInt(str[0]) < MAX_IP_PART) {
                if (Integer.parseInt(str[1]) < MAX_IP_PART) {
                    if (Integer.parseInt(str[2]) < MAX_IP_PART) {
                        if (Integer.parseInt(str[3]) < MAX_IP_PART) {
                            isIpFormat = true;
                        }
                    }
                }
            }
        }
        if (MAX_PORT < port) {
            isIpFormat = false;
        }
        return isIpFormat;
    }

    private static Properties readProperties() {
        Properties props = new Properties();
        InputStream in = Config.class.getClassLoader().getResourceAsStream(USER_DEFINED_PROPERTIES);

        if (in != null) {
            LOG.info("Reading configuration:" + USER_DEFINED_PROPERTIES);
            try {
                props.load(in);
            } catch (IOException exception) {
                LOG.error("Get Config from " + USER_DEFINED_PROPERTIES + " failed. Attempt to get from " + ESCONNECT_PROPERTIES, exception);
            } finally {
                try {
                    in.close();
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }
        }
        if (props.isEmpty()) {
            in = Config.class.getClassLoader().getResourceAsStream(ESCONNECT_PROPERTIES);
            try {
                LOG.info("Reading configuration:" + ESCONNECT_PROPERTIES);
                props.load(in);
            } catch (IOException exception) {
                LOG.error("Get Config from " + ESCONNECT_PROPERTIES + " failed.", exception);
            } finally {
                try {
                    in.close();
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }
        }
        return props;
    }

}
